﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DevExpress.Data.Filtering;
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Actions;
using DevExpress.ExpressApp.Editors;
using DevExpress.ExpressApp.Layout;
using DevExpress.ExpressApp.Model.NodeGenerators;
using DevExpress.ExpressApp.SystemModule;
using DevExpress.ExpressApp.Templates;
using DevExpress.ExpressApp.Utils;
using DevExpress.Persistent.Base;
using DevExpress.Persistent.Validation;
using DevExpress.XtraEditors;
using DevExpress.ExpressApp.PivotGrid.Win;
using DevExpress.XtraPivotGrid;
using System.Windows.Forms;
using aztall.Module.BusinessObjects;
using DevExpress.XtraEditors.Controls;
using DevExpress.Utils;
using aztall.Module.Controllers;
using DevExpress.ExpressApp.Xpo;
using DevExpress.Xpo;
using DevExpress.Xpo.Metadata;

namespace aztall.Module.Win.Controllers
{
	// For more typical usage scenarios, be sure to check out https://documentation.devexpress.com/eXpressAppFramework/clsDevExpressExpressAppViewControllertopic.aspx.
	public partial class PromoCost_Controller : ViewController
	{
		const string NullText = "Please select a value";
		IObjectSpace nestedOS;

		const string _PivotLayoutId = "PromoCost_AdvanceInput_PivotLayout";
		DetailView thisView = null;

		Promo _Promo = null;

		#region UI Controls
		CheckedComboBoxEdit customerControl, skuControl;
		LookUpEdit metricControl;
		CalcEdit rateControl;
		TextEdit descriptionControl = null;
		XafPivotGridControl pivotGridControl;
		SingleChoiceAction currentView;
		ParametrizedAction addView;
		SimpleAction removeView;

		SimpleAction DialogCancel;
		SimpleAction DialogOK;

		#endregion

		IList<BaseEdit> ToolTipControls = new List<BaseEdit>();
		string[] ValueActions = new string[] { "PivotAction_AddValue", "PivotAction_SetValue", "PivotAction_DistributeValue", "PivotAction_RemoveValue" };
		#region pivot fields
		PivotFieldItem[] pivotFieldItems = new PivotFieldItem[]
		{
			new PivotFieldItem {Area = PivotArea.RowArea, FieldCaption = "Sku",FieldName = "Sku.DisplayText"},
			new PivotFieldItem {Area = PivotArea.RowArea, FieldCaption = "Customer",FieldName = "Customer.DisplayText"},
			new PivotFieldItem {Area = PivotArea.RowArea, FieldCaption = "Description",FieldName = "Description"},

			new PivotFieldItem {Area = PivotArea.ColumnArea, FieldCaption = "Metric",FieldName = "Metric.DisplayText"},

			new PivotFieldItem {Area = PivotArea.DataArea, FieldCaption = "Rate",FieldName = "Rate", FormatType = FormatType.Numeric, FormatString = "#,0.00"},
			new PivotFieldItem {Area = PivotArea.DataArea, FieldCaption = "Line Cost",FieldName = "PromoLineCost", FormatType = FormatType.Numeric, FormatString = "#,0.00"}
		};

		#endregion
		BindingSource bsPivotGrid = new BindingSource();

		EventHandler setToolTip = (sender, e) => { var c = sender as BaseEdit; c.ToolTip = (c.EditValue == null) ? string.Empty : c.Text; };

		public PromoCost_Controller()
		{
			InitializeComponent();
			// Target required Views (via the TargetXXX properties) and create their Actions.
			base.TargetViewId = "PromoCost_AdvanceInput";
			base.TargetViewType = ViewType.DetailView;

		}
		protected override void OnActivated()
		{
			base.OnActivated();
			// Perform various tasks depending on the target View.
			if (nestedOS == null)
				nestedOS = base.ObjectSpace.CreateNestedObjectSpace();

			thisView = (DetailView)View;
			_Promo = nestedOS.GetObject<Promo>(thisView.CurrentObject as Promo);

			bsPivotGrid.DataSource = _Promo.PromoCosts;

			foreach (string item in ValueActions)
			{
				SimpleAction action = base.Frame.GetController<PivotActions_Controller>().Actions[item] as SimpleAction;
				action.Execute += SetValue;
			}

			currentView = base.Frame.GetController<PivotActions_Controller>().Actions["PivotAction_CurrentView"] as SingleChoiceAction;
			addView = base.Frame.GetController<PivotActions_Controller>().Actions["PivotAction_AddView"] as ParametrizedAction;
			removeView = base.Frame.GetController<PivotActions_Controller>().Actions["PivotAction_RemoveView"] as SimpleAction;
			currentView.Execute += CurrentView_Execute;
			removeView.Execute += RemoveView_Execute;
			addView.Execute += AddView_Execute;

			DialogOK = base.Frame.GetController<DialogController>().Actions["DialogOK"] as SimpleAction;
			DialogCancel = base.Frame.GetController<DialogController>().Actions["DialogCancel"] as SimpleAction;
			DialogOK.Execute += EndDialog;
			DialogCancel.Execute += EndDialog;
		}

		protected override void OnViewControlsCreated()
		{
			base.OnViewControlsCreated();
			// Access and customize the target View control.

			rateControl = thisView.FindItem("Rate").Control as CalcEdit;
			descriptionControl = thisView.FindItem("Description").Control as TextEdit;

			customerControl = thisView.FindItem("Customers").Control as CheckedComboBoxEdit;
			if (customerControl != null)
			{
				customerControl.Properties.DataSource = _Promo.Customers;
				customerControl.Properties.DisplayMember = "DisplayText";
				customerControl.Properties.ValueMember = "Oid";
				customerControl.Properties.NullValuePrompt = NullText;
				customerControl.Properties.NullValuePromptShowForEmptyValue = true;
				customerControl.CheckAll();
				ToolTipControls.Add(customerControl);
			}
			skuControl = thisView.FindItem("Skus").Control as CheckedComboBoxEdit;
			if (skuControl != null)
			{
				skuControl.Properties.DataSource = _Promo.Skus;
				skuControl.Properties.DisplayMember = "DisplayText";
				skuControl.Properties.ValueMember = "Oid";
				skuControl.Properties.NullValuePrompt = NullText;
				skuControl.Properties.NullValuePromptShowForEmptyValue = true;
				skuControl.CheckAll();
				ToolTipControls.Add(skuControl);
			}
			metricControl = thisView.FindItem("Metric").Control as LookUpEdit;
			if (metricControl != null)
			{
				metricControl.Properties.DataSource = MetricHelper.Metrics.Where(c => c.MetricType == MetricType.PromoCost);
				metricControl.Properties.DisplayMember = "DisplayText";
				metricControl.Properties.ValueMember = "Oid";
				metricControl.Properties.NullValuePrompt = NullText;
				metricControl.Properties.NullValuePromptShowForEmptyValue = true;

				metricControl.Properties.Columns.Add(new LookUpColumnInfo("MetricCode", "Metric Code"));
				metricControl.Properties.Columns.Add(new LookUpColumnInfo("MetricDesc", "Metric Description"));
				metricControl.EditValueChanged += MetricControl_EditValueChanged;
				ToolTipControls.Add(metricControl);
				metricControl.Properties.PopupFormMinSize = new System.Drawing.Size(400, 500);
			}

			currentView.Items.Clear();
			pivotGridControl = thisView.FindItem("PivotGrid").Control as XafPivotGridControl;
			if (pivotGridControl != null)
			{
				foreach (var item in pivotFieldItems)
				{
					var newItem = new PivotGridField(item.FieldName, item.Area);
					newItem.Caption = item.FieldCaption;
					if (item.FormatString != string.Empty)
					{
						newItem.CellFormat.FormatString = item.FormatString;
						newItem.CellFormat.FormatType = DevExpress.Utils.FormatType.Numeric;
					}
					newItem.GroupInterval = item.GroupInterval;

					if (newItem.FieldName == "Rate")
					{
						newItem.TotalsVisibility = PivotTotalsVisibility.None;
						newItem.Options.ShowGrandTotal = false;
						newItem.Options.ShowTotals = false;
					}
					pivotGridControl.Fields.Add(newItem);
				}
				pivotGridControl.DataSource = bsPivotGrid;

				IObjectSpace ios = base.ObjectSpace;
				IList<ConfigValue> configValues = ios.GetObjects<ConfigValue>(
								CriteriaOperator.Parse("Config.ConfigGroup = ? and Config.ConfigName = ?", ConfigGroup.Internal, _PivotLayoutId)
								);
				ConfigValue defaultView = configValues.Where(p => p.Value.ToUpper() == "Default".ToUpper()).FirstOrDefault();

				if (defaultView == null)    // create Default view
				{
					defaultView = PivotGridHelper.AddView(pivotGridControl, ios, ConfigGroup.Internal, _PivotLayoutId, "Default");
					ios.CommitChanges();
					configValues.Add(defaultView);
				}

				foreach (ConfigValue item in configValues)
				{
					currentView.Items.Add(new ChoiceActionItem(item.Oid.ToString(), item.Value, item));
				}

				addView.Value = defaultView.Value;
				currentView.SelectedItem = currentView.Items.Where(c => c.Id == defaultView.Oid.ToString()).First();
				PivotGridHelper.SetView(pivotGridControl, defaultView);
			}

			foreach (BaseEdit item in ToolTipControls)
			{
				item.ToolTip = (item.EditValue == null) ? string.Empty : item.Text;
				item.EditValueChanged += setToolTip;
			}
		}

		protected override void OnDeactivated()
		{
			foreach (string item in ValueActions)
			{
				SimpleAction action = base.Frame.GetController<PivotActions_Controller>().Actions[item] as SimpleAction;
				if (action != null)
					action.Execute -= SetValue;
			}
			currentView.Execute -= CurrentView_Execute;
			removeView.Execute -= RemoveView_Execute;
			addView.Execute -= AddView_Execute;
			DialogOK.Execute -= EndDialog;
			DialogCancel.Execute -= EndDialog;
			
			foreach (BaseEdit item in ToolTipControls)
				item.EditValueChanged -= setToolTip;
			// Unsubscribe from previously subscribed events and release other references and resources.
			base.OnDeactivated();
		}
		private void MetricControl_EditValueChanged(object sender, EventArgs arg)
		{
			metricControl.ToolTip = (metricControl.EditValue == null) ? string.Empty : metricControl.Text;
			var metric = (MetricHelper.MetricStruct)metricControl.GetSelectedDataRow();
			switch (metric.MetricScope)
			{
				case MetricScope.Promotion:
					customerControl.Enabled = false;
					skuControl.Enabled = false;
					customerControl.SetEditValue(null);
					skuControl.SetEditValue(null);
					break;
				case MetricScope.Customer:
					customerControl.Enabled = true;
					skuControl.Enabled = false;
					customerControl.CheckAll();
					skuControl.SetEditValue(null);
					break;
				case MetricScope.Sku:
					customerControl.Enabled = false;
					skuControl.Enabled = true;
					customerControl.SetEditValue(null);
					skuControl.CheckAll();
					break;

				case MetricScope.CustomerSku:
				default:
					customerControl.Enabled = true;
					skuControl.Enabled = true;
					customerControl.CheckAll();
					skuControl.CheckAll();
					break;

			}
		}
		private void SetValue(object sender, SimpleActionExecuteEventArgs e)
		{
			SimpleAction action = sender as SimpleAction;
			switch (action.Id)
			{
				case "PivotAction_AddValue":
					UpdateValue((oldValue, newValue) => oldValue + newValue);
					break;
				case "PivotAction_SetValue":
					UpdateValue((oldValue, newValue) => newValue);
					break;
				case "PivotAction_DistributeValue":
					UpdateValue((oldValue, newValue) => newValue);
					break;
				case "PivotAction_RemoveValue":
					RemoveValue();
					break;
			}
		}
		private void UpdateValue(Func<double, double, double> calculator)
		{
			IList<Customer> dsCust = customerControl.Properties.DataSource as IList<Customer>;
			IEnumerable<Customer> icustomers = customerControl.Properties
																							.Items
																							.Cast<CheckedListBoxItem>()
																							.Where(c => c.CheckState == CheckState.Checked)
																							.Select(x => dsCust[customerControl.Properties.Items.IndexOf(x)]) ?? Enumerable.Empty<Customer>();
			IList<Customer> customers = icustomers.ToList<Customer>();

			IList<Sku> dsSku = skuControl.Properties.DataSource as IList<Sku>;
			IEnumerable<Sku> iskus = skuControl.Properties
																							.Items
																							.Cast<CheckedListBoxItem>()
																							.Where(c => c.CheckState == CheckState.Checked)
																							.Select(x => dsSku[skuControl.Properties.Items.IndexOf(x)]) ?? Enumerable.Empty<Sku>();
			IList<Sku> skus = iskus.ToList<Sku>();
			Metric metric = nestedOS.GetObjectByKey<Metric>(metricControl.EditValue);
			String description = descriptionControl.Text;
			double newValue = (double)rateControl.Value;

			var q = (from cust in customers.DefaultIfEmpty()
							 from sku in skus.DefaultIfEmpty()
							 select new
							 {
								 Customer = cust,
								 Sku = sku,
								 Metric = metric,
								 Rate = newValue,
								 Description = description,
							 }).ToList();

			XPCollection<PromoCost> ds = _Promo.PromoCosts;

			//var updateItems = 
			ds.Where(c => q.Exists(x => (x.Customer?.Oid == c.Customer?.Oid && x.Sku?.Oid == c.Sku?.Oid && x.Metric.Oid == c.Metric.Oid)))
				.ToList()
				.ForEach(a => new Action(delegate () { a.Rate = calculator(a.Rate, newValue); a.Description = description; }).Invoke());

			var newItems = q.Where(c => !ds.Any(x => x.Customer?.Oid == c.Customer?.Oid && x.Sku?.Oid == c.Sku?.Oid && x.Metric.Oid == c.Metric.Oid)).ToList();
			foreach (var item in newItems)
			{
				PromoCost pc = nestedOS.CreateObject<PromoCost>();
				pc.Promo = _Promo;
				pc.Customer = item.Customer;
				pc.Sku = item.Sku;
				pc.Rate = calculator(0, item.Rate);
				pc.Description = item.Description;
				pc.Metric = item.Metric;
			}

			pivotGridControl.DataSource = null;
			pivotGridControl.DataSource = bsPivotGrid;
		}
		private void RemoveValue()
		{
			Metric metric = nestedOS.GetObjectByKey<Metric>(metricControl.EditValue);
			if (metric == null)
				return;

			IList<Customer> dsCust = customerControl.Properties.DataSource as IList<Customer>;
			IEnumerable<Customer> customers = customerControl.Properties
																							.Items
																							.Cast<CheckedListBoxItem>()
																							.Where(c => c.CheckState == CheckState.Checked)
																							.Select(x => dsCust[customerControl.Properties.Items.IndexOf(x)]) ?? Enumerable.Empty<Customer>();

			IList<Sku> dsSku = skuControl.Properties.DataSource as IList<Sku>;
			IEnumerable<Sku> skus = skuControl.Properties
																							.Items
																							.Cast<CheckedListBoxItem>()
																							.Where(c => c.CheckState == CheckState.Checked)
																							.Select(x => dsSku[skuControl.Properties.Items.IndexOf(x)]) ?? Enumerable.Empty<Sku>();

			var d = _Promo.PromoCosts.Where(c =>
											((customers.Count() == 0 && c.Customer == null) || (c.Customer != null && customers.Any(x => x.Oid == c.Customer.Oid))) &&
											((skus.Count() == 0 && c.Sku == null) || (c.Sku != null && skus.Any(x => x.Oid == c.Sku.Oid))) &&
											(c.Metric.MetricCode == metric.MetricCode))
											.ToList();
			nestedOS.Delete(d);

			pivotGridControl.DataSource = null;
			pivotGridControl.DataSource = bsPivotGrid;
		}
		private void AddView_Execute(object sender, ParametrizedActionExecuteEventArgs e)
		{
			if (e.ParameterCurrentValue == null)
				return;

			string viewName = e.ParameterCurrentValue.ToString();

			IObjectSpace ios = this.Application.CreateObjectSpace();

			ConfigValue configValue = PivotGridHelper.AddView(pivotGridControl, ios, ConfigGroup.Internal, _PivotLayoutId, viewName);

			ios.CommitChanges();

			ChoiceActionItem item = currentView.Items.Where(c => c.Id == configValue.Oid.ToString()).FirstOrDefault();
			if (item == null)
			{
				item = new ChoiceActionItem(configValue.Oid.ToString(), configValue.Value, configValue);
				currentView.Items.Add(item);
			}
			else
			{
				item.Caption = viewName;
				item.Data = configValue;
			}

			currentView.SelectedItem = item;
		}
		private void CurrentView_Execute(object sender, SingleChoiceActionExecuteEventArgs e)
		{
			ConfigValue configValue = e.SelectedChoiceActionItem.Data as ConfigValue;
			addView.Value = configValue.Value;
			PivotGridHelper.SetView(pivotGridControl, configValue);

		}
		private void RemoveView_Execute(object sender, SimpleActionExecuteEventArgs e)
		{
			if (currentView.SelectedItem == null)
				return;

			ConfigValue configValue = currentView.SelectedItem.Data as ConfigValue;
			IObjectSpace ios = this.Application.CreateObjectSpace();
			PivotGridHelper.DeleteView(ios, configValue);
			ios.CommitChanges();

			currentView.Items.Remove(currentView.SelectedItem);
			addView.Value = string.Empty;
		}
		private void EndDialog(object sender, SimpleActionExecuteEventArgs e)
		{
			if (nestedOS == null)
				throw new Exception("PromoCost_Controller.EndDialog: " + nameof(nestedOS) + " is null.");

			SimpleAction result = sender as SimpleAction;
			switch (result.Id)
			{
				case "DialogOK":
					try
					{
						nestedOS.CommitChanges();
					}
					catch
					{
						nestedOS.Rollback();
						throw;
					}
					break;
				case "DialogCancel":
					nestedOS.Rollback();
					break;
			}

			nestedOS.Dispose();
			nestedOS = null;

		}
	}
}

